package pdfsetter.core.schema.selectors;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import pdfsetter.core.schema.advanced.ConsecutiveFileListProcessPattern;
import pdfsetter.core.schema.advanced.FilePageSelection;
import pdfsetter.core.schema.advanced.FilePageSelector;
import pdfsetter.core.util.ArraysExtension;
import pdfsetter.core.util.RandomAccessIterableIterator;

/**
 * JoinedFilePageSelector joins other selectors. It works like in {@link ConsecutiveFileListProcessPattern}. Each selector can select some ordered set of pages
 * and joined selector just join all of this sets.
 * 
 * @author Andrzej Dorobisz
 */
public class JoinedFilePageSelector implements FilePageSelector {
    private final FilePageSelector[] selectors;

    public JoinedFilePageSelector(FilePageSelector... selectors) {
        this.selectors = selectors;
    }

    public JoinedFilePageSelector(List<FilePageSelector> pageSelectors) {
        selectors = new FilePageSelector[pageSelectors.size()];
        int i = 0;
        for (FilePageSelector curSelector : pageSelectors) {
            selectors[i++] = curSelector;
        }
    }

    @Override
    public FilePageSelection selectPages(int fileSize) {
        if (selectors.length <= 0 || fileSize <= 0)
            return new EmptyFilePageSelection();
        else {
            List<FilePageSelection> selections = new LinkedList<FilePageSelection>();
            for (int i = 0; i < selectors.length; i++) {
                FilePageSelection curSelection = selectors[i].selectPages(fileSize);
                if (curSelection.size() > 0)
                    selections.add(curSelection);
            }
            if (selections.size() > 0) {
                return new JoinedFilePageSelection(selections);
            } else
                return new EmptyFilePageSelection();
        }
    }

    private class JoinedFilePageSelection implements FilePageSelection {
        private final FilePageSelection[] pageSelections;
        private final int size;
        private final int[] selectionsBeginnings;

        public JoinedFilePageSelection(List<FilePageSelection> selections) { // at least one selection, all selections are non empty
            int length = selections.size();
            pageSelections = new FilePageSelection[length];
            int i = 0;
            for (FilePageSelection curSelection : selections) {
                pageSelections[i++] = curSelection;
            }
            selectionsBeginnings = new int[length];
            selectionsBeginnings[0] = 0;
            for (i = 1; i < length; i++)
                selectionsBeginnings[i] = selectionsBeginnings[i - 1] + pageSelections[i - 1].size();
            size = selectionsBeginnings[length - 1] + pageSelections[length - 1].size();
        }

        @Override
        public Integer get(int index) {
            if (index >= size || index < 0)
                throw new IndexOutOfBoundsException();
            int selectionNumber = ArraysExtension.floorSearch(selectionsBeginnings, index);
            return pageSelections[selectionNumber].get(index - selectionsBeginnings[selectionNumber]);
        }

        @Override
        public int size() {
            return size;
        }

        @Override
        public Iterator<Integer> iterator() {
            return new RandomAccessIterableIterator<Integer>(this);
        }
    }
}
